package com.itheima.wallet.user.service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.wallet.api.AccountService;
import com.itheima.wallet.common.domain.Account;
import com.itheima.wallet.common.domain.User;
import com.itheima.wallet.common.enums.AppHttpCodeEnum;
import com.itheima.wallet.common.mapper.UserMapper;
import com.itheima.wallet.common.vo.ResponseResult;
import com.itheima.wallet.user.api.UserService;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Reference
    private AccountService accountService;

    @Value("${wallet.token.salt}")
    private String salt;

    /**
     * 注册新用户
     *
     * @return
     */
    @Override
    public ResponseResult register(User user) {
        ResponseResult responseResult = null;
        //验证参数
        if (user == null) {
            //参数有误
            responseResult = ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
            return responseResult;
        }
        //查询数据库中是否已存在该用户
        User findUser = findUserByPhone(user.getPhone());
        if (findUser != null) {
            //数据库中已存在此手机号，返回-》数据已存在
            responseResult = ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST);
            return responseResult;
        }
        //保存新用户
        super.save(user);
        //开户初始化
        String account_id = user.getAccount_id();
        String username = user.getUsername();
        //开户
        createAccount(account_id, username);
        responseResult = ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        return responseResult;
    }

    private boolean createAccount(String account_id, String username) {
        //初始化账户信息
        Account account = Account.builder()
                .account_name(username)
                .account_id(account_id)
                .balance(0d)
                .build();
        return accountService.insertAccount(account);
    }

    /**
     * 根据手机号判断用户是否已经存在
     *
     * @param phone
     * @return
     */
    private User findUserByPhone(String phone) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone, phone);
        User findUser = super.getOne(lambdaQueryWrapper);
        return findUser;
    }

    /**
     * 登录
     *
     * @param phone 手机号
     * @param code  验证码
     * @return
     */
    @Override
    public ResponseResult login(String phone, String code) {
        ResponseResult responseResult = null;

        boolean checkResult = checkSMSCode(phone, code, "login");

        if (!checkResult) {
            responseResult = ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
            return responseResult;
        }

        //判断是否是新用户
        User user = findUserByPhone(phone);
        if (user == null) {
            //该手机号是新用户
            //默认密码
            String defaultPassword = phone + "123456";
            //雪花算法生成account_id
            Snowflake snowflake = IdUtil.getSnowflake(1, 1);
            String account_id = snowflake.nextIdStr();

            //封装默认新用户
            user = User.builder()
                    .username("默认用户名")
                    .phone(phone)
                    .password(SecureUtil.md5(defaultPassword))
                    .account_id(account_id)
                    .build();
            //存储新用户
            super.save(user);
            //开户初始化
            String username = user.getUsername();
            //开户
            createAccount(account_id, username);

        }
        //生成Token
        String token = getToken(user);
        responseResult = ResponseResult.okResult(token);
        return responseResult;
    }

    /**
     * 生成Token
     *
     * @param user
     * @return
     */
    private String getToken(User user) {
        //封装信息token
        HashMap<String, Object> claims = new HashMap<>();
        claims.put("phone", user.getPhone());
        claims.put("account_id", user.getAccount_id());
        //生成token
        String token = Jwts.builder()
                //存放数据
                .setClaims(claims)
                //使用指定的盐加密
                .signWith(SignatureAlgorithm.HS256, salt)
                //设置过期时间，12小时后过期
                .setExpiration(new DateTime().plusHours(12).toDate())
                .compact();
        return token;
    }

    /**
     * 发送动态验证码
     *
     * @param phone 手机号
     * @return
     */
    @Override
    public ResponseResult sendCode(String phone, String type) {
        ResponseResult responseResult = null;
        if (StringUtils.isNotBlank(phone)) {
            //验证手机号正则
            String regex = "^1(3|4|5|7|8)\\d{9}$";
            if (phone.matches(regex)) {
                //手机号符合规则
                String redisKey = type + "_" + phone;
                if (redisTemplate.hasKey(redisKey)) {
                    responseResult = ResponseResult.errorResult(501, "验证码还未过期");
                    return responseResult;
                }
                //生成6位随机验证码
                String code = RandomStringUtils.randomNumeric(6);
                //存入验证码，有效期5分钟
                redisTemplate.opsForValue().set(redisKey, code, 5, TimeUnit.MINUTES);
                responseResult = ResponseResult.okResult(code);
                return responseResult;
            }
        }
        //手机号不合规，返回警告
        responseResult = ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        return responseResult;
    }

    /**
     * 校验短信验证码
     *
     * @param phone
     * @param code
     * @return
     */
    @Override
    public boolean checkSMSCode(String phone, String code, String type) {
        //根据调用方生成对应的key
        String redisKey = type + "_" + phone;
        //得到正确的验证码
        String correctCode = redisTemplate.opsForValue().get(redisKey);
        if (correctCode != null && correctCode.equals(code)) {
            //验证码通过，销毁验证码
            redisTemplate.delete(redisKey);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 验证Token，返回用户信息
     * @param token
     * @return 返回用户的手机号和账户ID
     */
    @Override
    public User queryUserByToken(String token) {
        try {
            // 通过token解析数据
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(salt)
                    .parseClaimsJws(token)
                    .getBody();
            //解析出user的id和mobile
            String phone = body.get("phone").toString();
            String account_id = body.get("account_id").toString();
            User user = User.builder()
                    .phone(phone)
                    .account_id(account_id)
                    .build();

            return user;

        } catch (ExpiredJwtException e) {
            log.info("token已经过期！  token="+token);
        } catch (Exception e) {
            log.error("token不合法！  token="+token,e);
        }
        return null;
    }
}
